#pragma once 
#include "room.hpp"
#include "util.hpp"
#include "user_table.hpp"
#include "online_user_manager.hpp"

using room_ptr=std::shared_ptr<room>;//智能指针

//实现对所有的房间管理
class room_manager {
public:
    room_manager(user_table *tb_user, online_user_manager *online_user_manager)
    :_rid(1)
    ,_tb_user(tb_user)
    ,_online_user_manager(online_user_manager)
    {        
        DBG_LOG("room_manager init success");
    }
    ~room_manager()
    {
        DBG_LOG("room_manager destroy success");
    }
    //为两个用户创建房间，并且返回房间的智能指针管理对象
    room_ptr create_room(uint64_t uid1, uint64_t uid2)
    {
        //两个用户在游戏大厅中进行对战匹配，匹配成功后创建房间
        //1.校验两个用户是否在游戏大厅中，只有都在才需要创建房间
        if(!_online_user_manager->in_hall(uid1) )
        {
            DBG_LOG("用户%d不在游戏大厅中,创建房间失败", uid1);
            return room_ptr();
        }
          if(!_online_user_manager->in_hall(uid2) )
        {
            DBG_LOG("用户%d不在游戏大厅中,创建房间失败", uid2);
            return room_ptr();
        }

        //2.创建房间，将用户的信息添加到房间当中
        std::unique_lock<std::mutex> lock(_mutex);//加锁由于访问数据库
        room_ptr rp(new room(_rid, _tb_user, _online_user_manager));
        rp->add_black_id(uid1);
        rp->add_white_id(uid2); 
           DBG_LOG("创建房间 %lu: 黑棋玩家 %lu, 白棋玩家 %lu", _rid, uid1, uid2);

        //3.将房间信息管理起来
        _rooms.insert(std::make_pair(_rid, rp));
        _users.insert(std::make_pair(uid1, _rid));
        _users.insert(std::make_pair(uid2, _rid));
        
        _rid++;

        //4.返回房间信息
        return rp;
    }

    //通过房间id获取房间信息
    room_ptr get_room_by_rid(uint64_t rid)
    {
        //加锁保证下面的访问容器时是安全的
        std::unique_lock<std::mutex> lock(_mutex);
        auto it=_rooms.find(rid);
        if(it==_rooms.end())
        {
            return room_ptr();
        }
        return it->second;
    }

    // 通过用户id获取房间信息
    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto uit = _users.find(uid);
        if (uit == _users.end())
        {
            return room_ptr();
        }
        uint64_t rid = uit->second;
        // return get_room_by_rid(rid);   这样写可能会造成死锁
        auto it = _rooms.find(rid);
        if (it == _rooms.end())
        {
            return room_ptr();
        }
        return it->second;
    }

    //通过房间id销毁房间
    void destroy_room(uint64_t rid)
    {
        //1.通过房间id获取房间信息
        room_ptr rp=get_room_by_rid(rid);
        if(rp.get()==nullptr)
        {
            return;
        }
        //2.通过房间信息，获取房间中所有用户的id
        uint64_t uid1=rp->get_black_id();
        uint64_t uid2=rp->get_white_id();
        //3.移除房间管理中的用户信息
        std::unique_lock<std::mutex> lock(_mutex);
        _users.erase(uid1);
        _users.erase(uid2);
        //4.移除房间管理信息
        _rooms.erase(rid);
    }

    //删除房间中指定用户，如果房间中没有用户了，则销毁房间，用户连接断开时被调用
    void destroy_room_user(uint64_t uid)
    {
       //1.通过用户id获取房间信息
       room_ptr rp=get_room_by_uid(uid);
       if(rp.get()==nullptr)
       {
           return;
       }
       //2.通过房间信息，处理房间中玩家退出动作
       std::unique_lock<std::mutex> lock(_mutex);
       rp->handle_exit(uid);
       if(rp->player_num()==0)
       {
           destroy_room(rp->room_id());//没玩家了销毁房间
       }
       return ;
    }
private:
    uint64_t _rid;//房间号
    std::mutex _mutex;//锁
    user_table *_tb_user;//用户表
    online_user_manager *_online_user_manager;//在线用户管理
    std::unordered_map<uint64_t, room_ptr> _rooms;//房间号：房间智能指针
    std::unordered_map<uint64_t, uint64_t> _users;//用户id：房间号  
};